use std::borrow::Borrow;
use std::collections::HashMap;
use std::future::Future;
use std::ops::Add;
use std::sync::{Arc, Mutex};
use crossbeam_channel::Sender;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use crate::codes::tcp::TcpData;



pub struct TcpClientAsync{
    listener:TcpListener,
    stream:Option<TcpStream>,
    dict_fn:HashMap<String,fn(data:Vec<u8>)->Vec<u8>>,
    response:HashMap<String,Option<Sender<Vec<u8>>>>,
}

impl TcpClientAsync {
    pub async fn new(port:u32) -> Self {
        let mut addr = String::from("0.0.0.0");
        addr.push(':');
        addr = addr.add(port.to_string().as_str());

        let listener = TcpListener::bind(addr).await.unwrap();

        TcpClientAsync{
            listener,
            dict_fn: Default::default(),
            stream:None,
            response: Default::default(),
        }
    }
}

impl TcpClientAsync{
    pub async fn run(&mut self) {
        loop {
            let (mut stream,_) = match self.listener.accept().await {
                Ok(l) => l,
                Err(_)=>continue,
            };
            let res = self.list_by_stream(&mut stream).await;
            if let Err(e) = res {
                println!("tcp client err:{}",e);
                continue;
            }
            self.stream = Some(stream);
            break;
        }

        println!("begin loop");
        loop {
            self.read().await;
        }
    }

    pub fn add_fn(&mut self, tag: String, f:fn(data:Vec<u8>)->Vec<u8>) {
        self.dict_fn.insert(tag,f);
    }

    pub async fn write(&mut self, tag:&'static str, data:Vec<u8>) -> Result<Vec<u8>, String> {
        let tcp_data = TcpData::encode(tag.to_string(),data);
        let id = tcp_data.id();
        let res = self.stream.as_mut().unwrap().write(tcp_data.bytes().as_slice()).await;
        match res {
            Ok(_) => {
                let (sender, receive) = crossbeam_channel::bounded(50);
                self.response.insert(id, Some(sender));

                let recv = receive.recv();
                match recv {
                    Ok(data) => {
                        Ok(data)
                    },
                    Err(e) => { Err(e.to_string()) }
                }
            },
            Err(e) => { Err(e.to_string()) }
        }
    }

    async fn list_by_stream(&self, stream:&mut TcpStream) -> Result<(),String> {
        let tcp_data = TcpData::encode("hi".to_string(),Vec::new());
        let res = stream.write(tcp_data.bytes().as_slice()).await;
        if let Err(e) = res {
            return Err(e.to_string());
        }

        let mut buf = [0;1024*20];
        let hi_data = stream.read(&mut buf).await;
        if let Err(e) = hi_data {
            return Err(e.to_string());
        }

        if hi_data.unwrap() > 0 {
            return Ok(());
        }

        Err(String::from("error"))
    }


    async fn read(&mut self) -> Result<(),String>{
        let mut buf = [0;1024*20];
        let res = self.stream.as_mut().unwrap().read(&mut buf).await;
        match res {
            Ok(data)=> {
                let d = &buf[..data];
                println!("{}",String::from_utf8(d.to_vec()).unwrap());
                let new = TcpData::decode(d.to_vec());
                if new.tag().is_response() {
                    let id = new.id();
                    let sender = self.response.get_mut(&*id).unwrap().as_mut().unwrap();
                    sender.send(new.data().clone()).unwrap();
                } else {
                    if new.tag().string() == "probe" {
                        let response_data = new.response_data(vec![1]);
                        let write_res = self.stream.as_mut().unwrap().write(response_data.bytes().as_slice()).await;
                        if let Err(e) = write_res {
                            return Err(e.to_string());
                        }
                    } else {
                        let op = self.dict_fn.get(&*new.tag().string());
                        match op {
                            Some(f) => {
                                let mut response = f(new.data().clone());
                                let res_data =  new.response_data(response);
                                let write_res = self.stream.as_mut().unwrap().write(res_data.data()).await;
                                if let Err(e) = write_res {
                                    return Err(e.to_string());
                                }
                            },
                            None => {
                                return Err(String::from("function not found"));
                            }
                        }
                    }
                }

                return Ok(());
            },
            Err(e) => {return Err(e.to_string());}
        }

    }
}

#[cfg(test)]
mod test {
    use crate::client::asyncs::TcpClientAsync;

    #[tokio::test]
    async fn test() {
        let mut tcp = TcpClientAsync::new(5555).await;
        tcp.run().await;
    }
}